import whois
import logging
import argparse
from datetime import datetime

logger = None
popular_postfix = {
".com":"通用用途，最常见的后缀",
# ".org":"通常用于非盈利组织",
".net":"原本用于网络服务提供商，现在也广泛使用",
".cn":"china",
".top":"高端",
".ai":"安圭拉，人工智能",
".io":"代表印度洋"
}

top_leve_postfix = {
".com":"通用用途，最常见的后缀",
".org":"通常用于非盈利组织",
".net":"原本用于网络服务提供商，现在也广泛使用",
".edu":"用于教育机构",
".gov":"用于美国政府机构",
".mil":"用于美国军事机构",
".int":"用于国际组织",
".biz":"用于商业用途",
".info":"用于提供信息的网站",
".name":"用于个人或个体商业",
".co":"通常用于公司或商业网站",
".io":"特别流行于开发者和技术社区",
".me":"用于个人网站",
".tv":"用于视频或娱乐相关网站",
".shop":"用于电子商务网站",
".blog":"用于博客网站",
".site":"用于个人或公司网站",
".mobi":"移动设备网站",
".asia":"亚洲地区",
".info":"信息提供者",
".pro":"专业人士"

}

country_postfix = {
".cn":"中国",
".us":"美国",
".uk":"英国",
".jp":"日本",
".fr":"法国",
".de":"德国",
".ca":"加拿大",
".au":"澳大利亚",
".in":"(印度)",
".br":"(巴西)",
".it":"(意大利)",
".ca":"(加拿大)",
".es":"(西班牙)",
".ru":"(俄罗斯)",
".nl":"(荷兰)",
".kr":"(韩国)",
".mx":"(墨西哥)",
".ar":"(阿根廷)",
".za":"(南非)",
".se":"(瑞典)",
".ch":"(瑞士)",
".no":"(挪威)",
".fi":"(芬兰)",
".dk":"(丹麦)",
".pl":"(波兰)",
".pt":"(葡萄牙)",
".be":"(比利时)",
".cz":"(捷克)",
".hu":"(匈牙利)",
".gr":"(希腊)",
".tr":"(土耳其)",
".ro":"(罗马尼亚)",
".co":"(南非)",
".ae":"(阿联酋)",
".eg":"(埃及)",
".sa":"(沙特阿拉伯)",
".ir":"(伊朗)",
".pk":"(巴基斯坦)",
".iq":"(伊拉克)",
".kw":"(科威特)",
".om":"(阿曼)",
".ye":"(也门)",
".bh":"(巴林)",
".qa":"(卡塔尔)",
".jo":"(约旦)",
".lb":"(黎巴嫩)",
".ps":"(巴勒斯坦)",
".sy":"(叙利亚)",
".tn":"(突尼斯)",
".ma":"(摩洛哥)",
".dz":"(阿尔及利亚)",
".sn":"(塞内加尔)",
".mr":"(毛里塔尼亚)",
".gm":"(冈比亚)",
".gn":"(几内亚)",
".ci":"(科特迪瓦)",
".bf":"(布基纳法索)",
".ne":"(尼日尔)",
".ml":"(马里)",
".gh":"(加纳)",
".tg":"(多哥)",
".bj":"(贝宁)",
".ci":"(科特迪瓦)",
".cm":"(喀麦隆)",
".ga":"(加蓬)",
".gq":"(赤道几内亚)",
".ao":"(安哥拉)",
".cd":"(刚果民主共和国)",
".cg":"(刚果共和国)",
".rw":"(卢旺达)",
".bi":"(布隆迪)",
".sz":"(斯威士兰)",
".ls":"(莱索托)",
".mw":"(马拉维)",
".zm":"(赞比亚)",
".mz":"(莫桑比克)",
".na":"(纳米比亚)",
".ke":"(肯尼亚)",
".ug":"(乌干达)",
".tz":"(坦桑尼亚)",
".rw":"(卢旺达)",
".et":"(埃塞俄比亚)",
".so":"(索马里)",
".dj":"(吉布提)",
".er":"(厄立特里亚)",
".sd":"(苏丹)",
".tn":"(突尼斯)",
".ly":"(利比亚)",
".si":"(斯洛文尼亚)",
".hr":"(克罗地亚)",
".ba":"(波斯尼亚和黑塞哥维那)",
".mk":"(马其顿)",
".me":"(黑山)",
".al":"(阿尔巴尼亚)",
".ad":"(安道尔)",
".li":"(列支敦士登)",
".sm":"(圣马力诺)",
".va":"(梵蒂冈)",
".gi":"(直布罗陀)",
".cy":"(塞浦路斯)",
".is":"(冰岛)",
".fo":"(法罗群岛)",
".gl":"(格陵兰)",
".pm":"(圣皮埃尔和密克隆群岛)",
".lc":"(圣卢西亚)",
".vc":"(圣文森特和格林纳丁斯)",
".tt":"(特立尼达和多巴哥)",
".kn":"(圣基茨和尼维斯)",
".vg":"(英属维尔京群岛)",
".ms":"(蒙特塞拉特)",
".ai":"(安圭拉)",
".bm":"(百慕大)",
".gd":"(格恩西岛)",
".tc":"(特克斯和凯科斯群岛)",
".gp":"(瓜德罗普)",
".mq":"(马提尼克)",
".bl":"(圣巴泰勒米)",
".mf":"(圣马丁)",
".fr":"(法属圣马丁)",
".pf":"(法属波利尼西亚)",
".wf":"(瓦利斯和富图纳)",
".nc":"(新喀里多尼亚)",
".nu":"(瑙鲁)",
".nf":"(诺福克岛)",
".tv":"(图瓦卢)",
".fm":"(密克罗尼西亚联邦)",
".mp":"(北马里亚纳群岛)",
".pw":"(帕劳)",
".sb":"(所罗门群岛)",
".vu":"(瓦努阿图)",
".wf":"(瓦利斯和富图纳)",
".ki":"(基里巴斯)",
".to":"(汤加)",
".ws":"(萨摩亚)",
".tl":"(东帝汶)",
".sm":"(圣马力诺)",
".st":"(圣多美和普林西比)",
".sc":"(塞舌尔)",
}

chars = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
digitals = ['0','1','2','3','4','5','6','7','8','9']
# 声母
initial_consonants = ['b', 'p', 'm', 'f', 'd', 't', 'n', 'l', 'g', 'k', 'h', 'j', 'q', 'x', 'zh', 'ch', 'sh', 'r', 'z', 'c', 's', 'y', 'w']
# 韵母
# final_vowels = ['a', 'o', 'e', 'i', 'u', 'ü', 'ai', 'ei', 'ui', 'ao', 'ou', 'iu', 'ie', 'üe', 'ia', 'ua', 'uo', 'üe', 'iao', 'uai', 'uei', 'iao', 'iou', 'ian', 'uan', 'uen', 'iong', 'ang', 'eng', 'ing', 'ong']
final_vowels = ['a', 'o', 'e', 'i', 'u',  'ai', 'ei', 'ui', 'ao', 'ou', 'iu', 'ie',  'ia', 'ua', 'uo', 'ue', 'iao', 'uai', 'uei', 'iao', 'iou', 'ian', 'uan', 'uen', 'iong', 'ang', 'eng', 'ing', 'ong']
valid_combinations = {
    "b": ["a", "o", "i", "u", "ai", "ei", "ao", "ie", "an", "en", "in", "ang", "eng", "ing", "iao", "ian"], 
    "p": ["a", "o", "i", "u", "ai", "ei", "ao", "ou", "ie", "an", "en", "in", "ang", "eng", "ing", "iao", "ian"], 
    "m": ["a", "o", "e", "i", "u", "ai", "ei", "ao", "ou", "iu", "ie", "an", "en", "in", "ang", "eng", "ing", "iao", "ian"], 
    "f": ["a", "o", "u", "ei", "ou", "an", "en", "ang", "eng"], 
    "d": ["a", "e", "i", "u", "ai", "ei", "ui", "ao", "ou", "iu", "ie", "an", "en", "un", "ang", "eng", "ing", "ong", "ia", "iao", "ian", "uo", "uan"], 
    "t": ["a", "e", "i", "u", "ai", "ui", "ao", "ou", "ie", "an", "un", "ang", "eng", "ing", "ong", "iao", "ian", "uo", "uan"], 
    "n": ["a", "e", "i", "u", "ai", "ei", "ao", "ou", "iu", "ie", "ue", "an", "en", "in", "un", "ang", "eng", "ing", "ong", "iao", "ian", "iang", "uo", "uan"], 
    "l": ["a", "e", "i", "u", "ai", "ei", "ao", "ou", "iu", "ie", "ue", "an", "in", "un", "ang", "eng", "ing", "ong", "ia", "iao", "ian", "iang", "uo", "uan"], 
    "g": ["a", "e", "u", "ai", "ei", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "ua", "uo", "uai", "uan", "uang"], 
    "k": ["a", "e", "u", "ai", "ei", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "ua", "uo", "uai", "uan", "uang"], 
    "h": ["a", "e", "u", "ai", "ei", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "ua", "uo", "uai", "uan", "uang"], 
    "j": ["i", "u", "iu", "ie", "ue", "in", "un", "ing", "ia", "iao", "ian", "iang", "iong", "uan"], 
    "q": ["i", "u", "iu", "ie", "ue", "in", "un", "ing", "ia", "iao", "ian", "iang", "iong", "uan"], 
    "x": ["i", "u", "iu", "ie", "ue", "in", "un", "ing", "ia", "iao", "ian", "iang", "iong", "uan"], 
    "zh": ["a", "e", "i", "u", "ai", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "ua", "uo", "uai", "uan", "uang"], 
    "ch": ["a", "e", "i", "u", "ai", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "ua", "uo", "uai", "uan", "uang"], 
    "sh": ["a", "e", "i", "u", "ai", "ei", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ua", "uo", "uai", "uan", "uang"], 
    "r": ["e", "i", "u", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "uo", "uan"], 
    "z": ["a", "e", "i", "u", "ai", "ei", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "uo", "uan"], 
    "c": ["a", "e", "i", "u", "ai", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "uo", "uan"], 
    "s": ["a", "e", "i", "u", "ai", "ui", "ao", "ou", "an", "en", "un", "ang", "eng", "ong", "uo"], 
    "y": ["a", "e", "i", "u", "ao", "ou", "ue", "an", "in", "un", "ang", "ing", "ong", "uan", "uan"], 
    "w": ["a", "o", "u", "ai", "ei", "an", "en", "ang", "eng"]
    }
# {"b": ["ba", "bo", "bi", "bu", "bai", "bei", "bao", "bie", "ban", "ben", "bin", "bang", "beng", "bing", "biao"], "p": ["pa", "po", "pi", "pu", "pai", "pei", "pao", "pou", "pie", "pan", "pen", "pin", "pang", "peng", "ping", "piao"], "m": ["ma", "mo", "me", "mi", "mu", "mai", "mei", "mao", "mou", "miu", "mie", "man", "men", "min", "mang", "meng", "ming", "miao"], "f": ["fa", "fo", "fu", "fei", "fou", "fan", "fen", "fang"], "d": ["da", "de", "di", "du", "dai", "dei", "dui", "dao", "dou", "diu", "die", "dan", "den", "dun", "dang", "deng", "ding", "dong", "dia", "diao", "dian", "duo"], "t": ["ta", "te", "ti", "tu", "tai", "tui", "tao", "tou", "tie", "tan", "tun", "tang", "teng", "ting", "tong", "tiao", "tian", "tuo"], "n": ["na", "ne", "ni", "nu", "n\u00fc", "nai", "nei", "nao", "nou", "niu", "nie", "n\u00fce", "nan", "nen", "nin", "nun", "nang", "neng", "ning", "nong", "niao", "nian", "niang", "nuo"], "l": ["la", "le", "li", "lu", "l\u00fc", "lai", "lei", "lao", "lou", "liu", "lie", "l\u00fce", "lan", "lin", "lun", "lang", "leng", "ling", "long", "lia", "liao", "lian", "liang", "luo"], "g": ["ga", "ge", "gu", "gai", "gei", "gui", "gao", "gou", "gan", "gen", "gun", "gang", "geng", "gong", "gua", "guo", "guai", "guan"], "k": ["ka", "ke", "ku", "kai", "kei", "kui", "kao", "kou", "kan", "ken", "kun", "kang", "keng", "kong", "kua", "kuo", "kuai", "kuan"], "h": ["ha", "he", "hu", "hai", "hei", "hui", "hao", "hou", "han", "hen", "hun", "hang", "heng", "hong", "hua", "huo", "huai", "huan"], "j": ["ji", "ju", "jiu", "jie", "jue", "jin", "jun", "jing", "jia", "jiao", "jian", "jiang", "jiong"], "q": ["qi", "qu", "qiu", "qie", "que", "qin", "qun", "qing", "qia", "qiao", "qian", "qiang", "qiong"], "x": ["xi", "xu", "xiu", "xie", "xue", "xin", "xun", "xing", "xia", "xiao", "xian", "xiang", "xiong"], "zh": ["zha", "zhe", "zhi", "zhu", "zhai", "zhui", "zhao", "zhou", "zhan", "zhen", "zhun", "zhang", "zheng", "zhong", "zhua", "zhuo", "zhuai", "zhuan"], "ch": ["cha", "che", "chi", "chu", "chai", "chui", "chao", "chou", "chan", "chen", "chun", "chang", "cheng", "chong", "chua", "chuo", "chuai", "chuan"], "sh": ["sha", "she", "shi", "shu", "shai", "shei", "shui", "shao", "shou", "shan", "shen", "shun", "shang", "sheng", "shua", "shuo", "shuai", "shuan"], "r": ["re", "ri", "ru", "rui", "rao", "rou", "ran", "ren", "run", "rang", "reng", "rong", "ruo"], "z": ["za", "ze", "zi", "zu", "zai", "zei", "zui", "zao", "zou", "zan", "zen", "zun", "zang", "zeng", "zong", "zuo"], "c": ["ca", "ce", "ci", "cu", "cai", "cui", "cao", "cou", "can", "cen", "cun", "cang", "ceng", "cong", "cuo"], "s": ["sa", "se", "si", "su", "sai", "sui", "sao", "sou", "san", "sen", "sun", "sang", "seng", "song"], "y": ["ya", "ye", "yi", "yu", "yao", "you", "yue", "yan", "yin", "yun", "yang", "ying", "yong", "yuan"], "w": ["wa", "wo", "wu", "wai", "wei", "wan", "wen", "wang"]}
digitals = [0,1,2,3,4,5,6,7,8,9]
def generate_digital_domain(digital_count):
    domains = []
    upper = 10**digital_count 
    for i in range(upper):
        fmt = '%0{}d'.format(digital_count)
        domains.append(fmt % (i))
    return domains

def generate_char_domain(char_count):
    if char_count <= 0:
        return None
    elif char_count == 1:
        return chars
    else:
        return append_chars(chars, char_count-1)
def append_chars(src_chars, char_count):
    if char_count == 1:
        new_chars = []
        for ch in src_chars:
            gen_list = []
            for c in chars:
                gen_list.append(f"{ch}{c}")
            new_chars.extend(gen_list)
        return new_chars
    else:
        return append_chars(append_chars(src_chars, 1), char_count-1)

def append_digitals(src_digital, char_count):
    if char_count == 1:
        new_chars = []
        for ch in src_digital:
            gen_list = []
            for c in digitals:
                gen_list.append(f"{ch}{c}")
            new_chars.extend(gen_list)
        return new_chars
    else:
        return append_chars(append_digitals(src_digital, 1), char_count-1)

def generate_pinyin():
    """生成所有的汉语拼音，只包含标准读音且符合规范"""
    # 规范的组合
    # 生成拼音
    pinyin_list = []
    for initial in initial_consonants:
        for final in valid_combinations.get(initial, []):
            pinyin = initial + final
            pinyin_list.append(pinyin)

    # 处理特殊情况
    # pinyin_list.extend(['a', 'o', 'e', 'i', 'u', 'ü'])  # 单独的韵母
    pinyin_list.extend(['a', 'o', 'e', 'i', 'u'])  # 单独的韵母
    pinyin_list.extend(['er'])  # 轻声的“er”

    return pinyin_list

def append_pinyin(src_pys, py_count):
    if py_count == 1:
        new_pys = []
        concat_set = generate_pinyin()
        for py in src_pys:
            gen_list = []
            for concat in concat_set:
                gen_list.append(f"{py}{concat}")
            new_pys.extend(gen_list)
        return new_pys
    else:
        return append_pinyin(append_pinyin(src_pys,1), py_count-1)
    
def generate_pinyin_domain(py_count):
    if py_count <= 0:
        return None
    init_pys = generate_pinyin()
    if py_count == 1:
        return init_pys 
    else:
        return append_pinyin(init_pys, py_count-1)

def init_log(log_lv, logfile):
    lvs = log_level_map.keys()
    valid_lv = log_lv  in lvs 
    if not valid_lv:
        print(f"init log failed! not support log level{log_lv}")
        return
    format='%(asctime)s %(name)s [%(pathname)s line:%(lineno)d] %(levelname)s %(message)s'
    logger = logging.getLogger("main")
    logger.setLevel(logging.INFO)
    console_handler = logging.StreamHandler()
    file_handler = logging.FileHandler(filename=logfile)
    standard_fmt = logging.Formatter(format)
    console_handler.setFormatter(standard_fmt)
    file_handler.setFormatter(standard_fmt)
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)
    return logger


def domain_exist(domain_name):
    try:
        domain = whois.whois(domain_name)
        return True
    except:
        return False 
def check_domains(doms, writer=None):
    global logger
    leaked_domains = []
    for dom in doms:
        if not domain_exist(dom):
            leaked_domains.append(dom)
            logger.info(f"***-------------{dom} can be applied now!----------------**")
            if writer is not None:
                writer.write(f"{dom}\n")
                writer.flush()
        else:
            logger.info(f"{dom} already exists")
    return leaked_domains

# mode 1:popular  2 all
def check_digital_domain(digital_count, mode=1):
    global logger
    if mode == 1:
        postfix = popular_postfix.keys()
    elif mode == 2:
        postfix = top_leve_postfix.keys()
    else:
        postfix = None
        logger.error(f"no support mode:{mode}")
        return
    now_time = datetime.now().strftime("%Y%m%d%H%M%S")
    writer = open(f"digital_{digital_count}_{now_time}.txt", "a")
    total_domains = []
    checknames = generate_digital_domain(digital_count)
    for dom_name in checknames:
        dom_name = str(dom_name)
        for pf in postfix:
            total_domains.append(f"{dom_name}{pf}")
    leaked_doms = check_domains(total_domains, writer=writer)

# mode 1:popular  2 all
def check_chars_domain(char_count, mode=1):
    global logger
    if mode == 1:
        postfix = popular_postfix.keys()
    elif mode == 2:
        postfix = top_leve_postfix.keys()
    else:
        postfix = None
        logger.error(f"no support mode:{mode}")
        return
    now_time = datetime.now().strftime("%Y%m%d%H%M%S")
    writer = open(f"char_{char_count}_{now_time}.txt", "a")
    total_domains = []
    checknames = generate_char_domain(char_count)
    for dom_name in checknames:
        # dom_name = str(dom_name)
        for pf in postfix:
            total_domains.append(f"{dom_name}{pf}")
    leaked_doms = check_domains(total_domains, writer=writer)

def check_pinyin_domain(py_count, mode=1):
    global logger
    if mode == 1:
        postfix = popular_postfix.keys()
    elif mode == 2:
        postfix = top_leve_postfix.keys()
    else:
        postfix = None
        logger.error(f"no support mode:{mode}")
        return
    now_time = datetime.now().strftime("%Y%m%d%H%M%S")
    writer = open(f"pinyin_{py_count}_{now_time}.txt", "a")
    total_domains = []
    checknames = generate_pinyin_domain(py_count)
    for dom_name in checknames:
        # dom_name = str(dom_name)
        for pf in postfix:
            total_domains.append(f"{dom_name}{pf}")
    leaked_doms = check_domains(total_domains, writer=writer)

def check2n1c_domain(mode=1):  #2数字1字母
    global logger
    if mode == 1:
        postfix = popular_postfix.keys()
    elif mode == 2:
        postfix = top_leve_postfix.keys()
    else:
        postfix = None
        logger.error(f"no support mode:{mode}")
        return
    now_time = datetime.now().strftime("%Y%m%d%H%M%S")
    writer = open(f"2dig_1char_{now_time}.txt", "a")
    total_domains = []
    digital2_names = generate_digital_domain(2)
    checknames = append_chars(digital2_names, 1)
    for dom_name in checknames:
        dom_name = str(dom_name)
        for pf in postfix:
            total_domains.append(f"{dom_name}{pf}")
    leaked_doms = check_domains(total_domains, writer=writer)

def check2c1n_domain(mode=1):  #2字母1数字
    global logger
    if mode == 1:
        postfix = popular_postfix.keys()
    elif mode == 2:
        postfix = top_leve_postfix.keys()
    else:
        postfix = None
        logger.error(f"no support mode:{mode}")
        return
    now_time = datetime.now().strftime("%Y%m%d%H%M%S")
    writer = open(f"2dig_1char_{now_time}.txt", "a")
    total_domains = []
    # digital2_names = generate_digital_domain(2)
    # checknames = append_char(digital2_names, 1)
    char_names = generate_char_domain(2)
    checknames = append_digitals(char_names, 1)
    for dom_name in checknames:
        dom_name = str(dom_name)
        for pf in postfix:
            total_domains.append(f"{dom_name}{pf}")
    leaked_doms = check_domains(total_domains, writer=writer)

log_level_map = {
    "debug":10,
    "info":20,
    "warn":30,
    "error":40,
    "fatal":50
}

def main():
    parser = argparse.ArgumentParser(description="domain detecter")
    parser.add_argument("--loglevel", type=str, default="info", help="[debug,info, warn,error,fatal]")
    parser.add_argument("--domrange", type=int, default=1, help="1 popular domain postfix, 2 country 3 all")
    parser.add_argument("--mode", type=int, default=0, help="0 all, 1 digital, 2 character, 3 pinyin, 4 2char1n, 5 2n1c")
    parser.add_argument("--count", type=int, default=3, help="the count to generate the domain according to the mode")
    parser.add_argument("--log", type=str, default="domain.log", help="log file name")
    # parser.add_argument("--log", type=str, default="domain.log", help="log file name")

    args = parser.parse_args()
    global logger
    logger = init_log(args.loglevel, args.log)
    if args.mode == 0 :
        check_digital_domain(3)
        check_digital_domain(4)
        # check_chars_domain(2)
        check_chars_domain(3)
        check_chars_domain(4)
        check_pinyin_domain(2)
        check2c1n_domain()
        check2n1c_domain()
    elif args.mode == 1:
        check_digital_domain(args.count, args.domrange)
    elif args.mode == 2:
        check_chars_domain(args.count, args.domrange)
    elif args.mode == 3:
        check_pinyin_domain(args.count, args.domrange)
    elif args.mode == 4:
        check2c1n_domain(args.domrange)
    elif args.mode == 5:
        check2n1c_domain(args.domrange)
    else:
        print(f"params not valid")

if __name__ == '__main__':
    main()